package com.macro.mall.tiny.component.wx;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.binarywang.spring.starter.wxjava.pay.properties.WxPayProperties;
import com.github.binarywang.wxpay.bean.merchanttransfer.TransferCreateRequest;
import com.github.binarywang.wxpay.bean.notify.OriginNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.bean.transfer.TransferBatchesRequest;
import com.github.binarywang.wxpay.bean.transfer.TransferBatchesResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.util.AesUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.macro.mall.tiny.component.wx.entity.bo.WxBO;
import com.macro.mall.tiny.component.wx.entity.bo.WxPayBO;
import com.macro.mall.tiny.component.wx.entity.bo.WxTransferBO;
import com.macro.mall.tiny.modules.oms.model.OmsSettleApply;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.ConstructorUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.cache.CachesEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import springfox.documentation.annotations.ApiIgnore;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;

import static com.alibaba.druid.util.FnvHash.Constants.GSON;

/**
 * @author yelong.xie
 * @date 2024/7/3 18:38
 */
@Component
@Slf4j
public class WxClient {
    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private WxPayService wxPayService;

    @Autowired
    private TransferBatchService transferBatchService;


    private static final String TRANSFER_NOTIFY_URL = "https://www.weixiuzhushou.top/admin/oms/pay/notify/transfer";

    /**
     * 一个jscode只能用一次
     * {"openid":"odpJZ5eKn40O3bgOmtMe_HoUnBjo","sessionKey":"n8pOpJHAlJzN8ALpiKMAeQ=="}
     * {"errcode":40163,"errmsg":"code been used, rid: 66852c1a-130e6576-13027817"}
     *
     * @throws Exception
     */
    public WxBO getSessionInfo(String jsCode) {
        WxBO wxBO = new WxBO();
        try {
            WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(jsCode);
            wxBO.setSessionKey(sessionInfo.getSessionKey());
            wxBO.setUnionid(sessionInfo.getUnionid());
            wxBO.setOpenid(sessionInfo.getOpenid());
        } catch (Exception e) {
            wxBO.setSuccess(Boolean.FALSE);
            wxBO.setErrorMsg(e.getMessage());
            log.error("com.macro.mall.portal.component.wx.WxClient#getSessionInfo error: {}", e.getMessage());
        }
        return wxBO;
    }

    /**
     * 获取用户id
     *
     * @param code
     * @return
     */
    public WxBO getUserPhone(String code) {
        WxBO wxBO = new WxBO();
        try {
            WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getPhoneNoInfo(code);
            wxBO.setPurePhone(phoneNoInfo.getPurePhoneNumber());
        } catch (Exception e) {
            wxBO.setSuccess(Boolean.FALSE);
            wxBO.setErrorMsg(e.getMessage());
            log.error("com.macro.mall.portal.component.wx.WxClient#getUserPhone error: {}", e.getMessage());
        }
        return wxBO;
    }

    /**
     * 构建订单
     *
     * @param payBO
     * @return
     */
    public WxBO buildOrder(WxPayBO payBO) {
        WxBO wxBO = new WxBO();
        try {
            WxPayUnifiedOrderV3Request request = payBO.buildUnifiedOrderRequest();
            log.info("com.macro.mall.portal.component.wx.WxClient#buildOrder request: {}", request);
            WxPayUnifiedOrderV3Result.JsapiResult result = wxPayService.createOrderV3(TradeTypeEnum.JSAPI, request);
            log.info("com.macro.mall.portal.component.wx.WxClient#buildOrder result: {}", result);
            wxBO.setAppId(result.getAppId());
            wxBO.setNonceStr(result.getNonceStr());
            wxBO.setPackageValue(result.getPackageValue());
            wxBO.setPaySign(result.getPaySign());
            wxBO.setSignType(result.getSignType());
            wxBO.setTimeStamp(result.getTimeStamp());
            return wxBO;
        } catch (Exception e) {
            wxBO.setSuccess(Boolean.FALSE);
            wxBO.setErrorMsg(e.getMessage());
            log.error("com.macro.mall.portal.component.wx.WxClient#buildOrder error: {}", e.getMessage());
        }
        return wxBO;
    }

    /**
     * 解析订单通知结果
     *
     * @param data
     * @return
     */
    public WxBO parseOrderNotifyResult(String data) {
        WxBO wxBO = new WxBO();
        try {
            WxPayNotifyV3Result wxPayNotifyV3Result = wxPayService.parseOrderNotifyV3Result(data, null);
            log.info("com.macro.mall.portal.component.wx.WxClient#parseOrderNotifyResult wxPayOrderNotifyResult: {}", wxPayNotifyV3Result);
            WxPayBO extract = WxPayBO.extract(wxPayNotifyV3Result);
            log.info("extract: {}", extract);
            wxBO.setWxPayBO(extract);
            return wxBO;
        } catch (WxPayException e) {
            wxBO.setSuccess(Boolean.FALSE);
            wxBO.setErrorMsg(e.getMessage());
            log.error("com.macro.mall.portal.component.wx.WxClient#parseOrderNotifyResult error: {}", e.getMessage());
        }
        return wxBO;
    }

    /**
     * 用户提现
     *
     * @param data
     * @param header
     * @return
     */
    public WxBO transfer(OmsSettleApply apply) {
        WxBO wxBO = new WxBO();
        try {
            InitiateBatchTransferRequest request =
                    new InitiateBatchTransferRequest();
            request.setAppid(wxPayService.getConfig().getAppId());
            request.setTotalAmount(apply.getFee().longValue());
            request.setTotalNum(1);
            request.setOutBatchNo(apply.getSettleOrderId());
            request.setBatchName("用户提现");
            request.setBatchRemark("用户提现");

            List<TransferDetailInput> transferDetailListList = new ArrayList<>();

            TransferDetailInput detail = new TransferDetailInput();
            detail.setOpenid(apply.getOpenId());
            detail.setOutDetailNo(apply.getSettleOrderId());
            detail.setTransferAmount(apply.getFee().longValue());
            detail.setTransferRemark("用户提现" + convertFenToYuan(apply.getFee()) + "元");
            transferDetailListList.add(detail);

            request.setTransferDetailList(transferDetailListList);
            request.setNotifyUrl(TRANSFER_NOTIFY_URL);
            log.info("com.macro.mall.portal.component.wx.WxClient#transfer apply:{} , request: {}", JSONUtil.toJsonStr(apply), JSONUtil.toJsonStr(request));
            InitiateBatchTransferResponse response = transferBatchService.initiateBatchTransfer(request);
            log.info("com.macro.mall.portal.component.wx.WxClient#transfer apply:{} , response: {}", JSONUtil.toJsonStr(apply), JSONUtil.toJsonStr(response));
            wxBO.setSuccess(Boolean.TRUE);
        } catch (Exception e) {
            wxBO.setSuccess(Boolean.FALSE);
            wxBO.setErrorMsg(e.getMessage());
            log.error("com.macro.mall.portal.component.wx.WxClient#transfer apply:{} , error: {}", apply, e.getMessage());
        }
        return wxBO;
    }

    private static final Gson GSON = new GsonBuilder().create();

    /**
     * {
     *   out_batch_no=1862783705110683648,
     *   batch_id=131000610018306928233582024113031953307235,
     *   batch_status=FINISHED,
     *   total_num=1.0,
     *   total_amount=1.0,
     *   success_amount=0.0,
     *   success_num=0.0,
     *   fail_amount=1.0,
     *   fail_num=1.0,
     *   mch_id=1689887782,
     *   update_time=2024-11-30T17: 19: 40+08: 00,
     *   mchid=1689887782
     * }
     * @param notifyData
     * @return
     */
    public WxBO parseTransferNotify(String notifyData) {
        WxBO bo = new WxBO();
        WxTransferBO transferBO = new WxTransferBO();
        bo.setWxTransferBO(transferBO);
        try {
            OriginNotifyResponse response = GSON.fromJson(notifyData, OriginNotifyResponse.class);
            OriginNotifyResponse.Resource resource = response.getResource();
            String cipherText = resource.getCiphertext();
            String associatedData = resource.getAssociatedData();
            String nonce = resource.getNonce();
            String apiV3Key = wxPayService.getConfig().getApiV3Key();
            String result = AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key);
            LinkedHashMap decryptNotifyResult = GSON.fromJson(result, LinkedHashMap.class);
            transferBO.setOutBatchNo((String) decryptNotifyResult.get("out_batch_no"));
            transferBO.setSuccess((Double) decryptNotifyResult.get("fail_num") > 0 ? Boolean.FALSE : Boolean.TRUE);
            transferBO.setErrorMsg(transferBO.getSuccess()? null: "微信转账失败");
            decryptNotifyResult.get("out_batch_no");
            log.info("com.macro.mall.portal.component.wx.WxClient#parseTransferNotify result: {}", JSON.toJSONString(decryptNotifyResult));
        } catch (Exception e) {
            log.info("com.macro.mall.portal.component.wx.WxClient#parseTransferNotify error: {}", e.getMessage());
            bo.setSuccess(Boolean.FALSE);
            bo.setErrorMsg(e.getMessage());
        }
        return bo;
    }

    public String convertFenToYuan(Integer fee) {
        Double v = new Double(fee);
        BigDecimal bigDecimal = new BigDecimal(v).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
        return bigDecimal.toString();
    }

    public static void main(String[] args) {
        Integer fee = 120;
        Double v = new Double(fee);
        BigDecimal bigDecimal = new BigDecimal(v).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
        System.out.println(bigDecimal.toString());
    }
}
